package com.wawu.common.IOC;

import com.wawu.common.annotation.IOC.Autowired;
import com.wawu.common.annotation.IOC.component.Component;
import com.wawu.common.annotation.IOC.component.RestController;
import com.wawu.common.annotation.controller.globalExceptionHandle.RestControllerAdvice;
import com.wawu.common.utils.AnnotationUtils;
import lombok.Getter;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 实现IOC容器(控制反转和依赖注入)
 * 表现：使用@Component及其相关注解注册bean对象，然后使用@Autowired进行注入
 */
public class ApplicationContext {
    private Set<Class<? extends Annotation>> registerComponentAnnotations = new HashSet<>();//存放被@Component修饰的注解
    private Map<Class<?>, Object> beanMap = new HashMap<>();  // key:类；value:实例；（单例模式）
        //这个类似工厂模式
    @Getter
    private Set<Class<?>> restControllerClazzes = new HashSet<>();//单独再拿出被@RestController修饰的类，后续还需要注册路由
    @Getter
    private Set<Class<?>> restControllerAdviceClazzes=new HashSet<>();//存放所有的全局异常处理器
    public ApplicationContext(String basePackage,String baseAnnotationPackage) {
        // 1. 获取所有自定义注解，并筛选出需要注册的注解(这里逻辑上可能还有问题)
        scanForRegisterAnnotations(baseAnnotationPackage);
        System.out.println("@Component相关注解:"+registerComponentAnnotations);
        // 2. 扫描并注册组件类
        scanAndRegisterComponents(basePackage);
        System.out.println("相关bean对象："+beanMap);
        // 3. 执行依赖注入
        injectDependencies();
        System.out.println("执行依赖注入成功。。。");
    }

    /**
     * 扫描所有自定义注解，筛出所有@Component(包括本身)相关注解
     * @param baseAnnotationPackage:注解基础包(只扫描这个包)
     */
    private void scanForRegisterAnnotations(String baseAnnotationPackage) {
        //把@Component本身加入
        registerComponentAnnotations.add(Component.class);
        //递归扫描所有被@Component修饰的注解
        Reflections reflections = new Reflections(baseAnnotationPackage);
        Set<Class<?>> annotations = reflections.getTypesAnnotatedWith(Component.class);
                //TODO:可能有问题：是否能扫描到多层嵌套的注解，虽然这里不知道为什么是能够扫描到的
//        System.out.println(annotations);
        for (Class<?> annotation : annotations) {
            //判断是否为注解
            if(annotation.isAnnotation()){
                //通过自定义的注解工具类递归判断是否被@Component修饰
                if(AnnotationUtils.isAnnotationPresent(annotation,Component.class,true)){//注意：这里使用正常判断是不会递归判断，只能看最外层
                    registerComponentAnnotations.add((Class<? extends Annotation>) annotation);
                }
            }
        }
    }

    /**
     * 扫描同时实例化注册bean对象
     * @param basePackage:当前需要扫描的包
     */
    private void scanAndRegisterComponents(String basePackage) {
        Reflections reflections = new Reflections(basePackage);//只扫描这个包
        //1. 遍历跟@Component相关的注解
        for(Class<? extends Annotation> registerComponentAnnotation:registerComponentAnnotations){
//            System.out.println("当前注解："+registerComponentAnnotation);
            //2.获取被这个注解修饰的所有类同时进行判断是否需要实例话
            Set<Class<?>> componentWithComponentAnnotated = reflections.getTypesAnnotatedWith(registerComponentAnnotation);//注意不会识别嵌套注解
//            System.out.println(registerComponentAnnotation.getName()+":"+componentWithComponentAnnotated);
            for (Class<?> clazz : componentWithComponentAnnotated) {
                //如果为一个普通类，则进行注册实例化
                if(!clazz.isInterface() && !clazz.isEnum() && !clazz.isPrimitive() && !clazz.isArray()){
                    //进行实例化注册
                    try {
                        Object bean = clazz.getDeclaredConstructor().newInstance();
                        beanMap.put(clazz, bean);  // 将类及其实例包装对象加入beanMap
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                    //获取被@RestController修饰的类，后需要注册路由
                        //注意下：这里就不递归检查了
                    if(clazz.isAnnotationPresent(RestController.class)){
                        restControllerClazzes.add(clazz);

                    }else if (clazz.isAnnotationPresent(RestControllerAdvice.class)){//放全局异常处理器类
                        restControllerAdviceClazzes.add(clazz);
                    }
                }
            }
        }
    }

    /**
     * 执行依赖注入（简化后）
     * 规则：需要为bean对象（被@Component修饰），会将被@Autowired修饰的属性注入
     *  - 普通类型：直接根据类型注入
     *  - 接口类型：当实现类只有一个时，会将这个注入
     *
     */
    private void injectDependencies() {
        //遍历所有bean对象（就是被@Component修饰）
        for(Map.Entry<Class<?>,Object> entry: beanMap.entrySet()){
            Class<?> clazz = entry.getKey();
            Object bean = entry.getValue();

            //获取所有字段，检查是否有@Autowired注解
            for(Field field:clazz.getDeclaredFields()){
                if (field.isAnnotationPresent(Autowired.class)){
                    //1. 寻找其需要注入的依赖(实体)
                        //如果字段类型是接口，则需要根据接口类型获取实现类
                        //如果不是，则直接从注册的bean对象获取
                    Object dependency = null;
                        //获取字段类型
                    Class<?> fieldType=field.getType();
                    if(fieldType.isInterface()){
                        // 获取所有实现该接口的类
                        Set<Class<?>> implementors = getImplementors(fieldType);
                        //只能有一个实现，不然不好注入
                        if (implementors.size() == 1) {
                            //这里依然是从bean，而不是新建了
//                                Object dependency = implementors.iterator().next().getDeclaredConstructor().newInstance();//新建
                            dependency=beanMap.get(implementors.iterator().next());//TODO 探究：这里可以看看为什么不需要.getClass()
                        } else {
                            System.err.println("找到多个实现或者没有实现接口的类: " + fieldType.getName());
                        }
                    }else{
                        // 如果字段类型不是接口，直接从 beanMap 获取
                        dependency = beanMap.get(fieldType);
                    }
                    //2.注入（赋值）给这个字段
                    if (dependency != null) {
                        try {
                            field.setAccessible(true);
                            field.set(bean, dependency);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 如果找不到匹配的依赖
                        System.err.println("无法注入依赖: " + fieldType.getName());
                    }
                }
            }

        }

    }

    /**
     * 获取接口类型的所有实现类
     * @param interfaceType:接口的类型
     * @return:当前接口所有的实现类
     */
    private Set<Class<?>> getImplementors(Class<?> interfaceType) {
//        Set<Class<?>> implementors = new HashSet<>();
//        // 扫描所有已注册的 Bean，查找实现了该接口的类
//        for (Class<?> clazz : beanMap.keySet()) {
//            if (interfaceType.isAssignableFrom(clazz) && !clazz.equals(interfaceType)) {
//                implementors.add(clazz);
//            }
//        }
//        return implementors;
        //这里像试试流，作用跟上面一样
        return beanMap.keySet().stream()
                .filter(clazz->{
                    return interfaceType.isAssignableFrom(clazz) && !clazz.equals(interfaceType);
                        //`isAssignableFrom` 是 Java 反射 API 中的一个方法，判断 interfaceType（通常为接口或父类）是否是 clazz 的超类或超接口，或 clazz 是否可以赋值给 interfaceType 类型的变量。
                })
                .collect(Collectors.toSet());
    }


    /**
     * 获取bean对象
     * @param clazz
     * @return
     * @param <T>
     */
    public <T> T getBean(Class<T> clazz) {
        // 从 beanMap 中查找指定类的实例
        Object bean = beanMap.get(clazz);

        // 如果找不到该类型的 Bean，可以考虑抛出异常或返回 null
        if (bean == null) {
            throw new IllegalArgumentException("No bean found for class: " + clazz.getName());
        }

        return clazz.cast(bean); // 返回转换为指定类型的 bean
    }



}
